package com.example.autumn.io;

import jakarta.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.*;
import java.util.*;
import java.util.function.Function;

/**
 * @author liuzhiyong
 * @date 2023/10/17
 * description: 配置解析
 */
public class PropertyResolver {

    Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 存储配置项和环境变量
     */
    Map<String, String> properties = new HashMap<>();

    /**
     * value的类型转换器map
     */
    Map<Class<?>, Function<String, Object>> converters = new HashMap<>();



    /**
     * PropertyResolver构造函数
     * 创建PropertyResolver对象, 存入环境变量和配置项
     *
     * @param props 配置的属性
     * @author liuzhiyong
     * @date 2023/10/18
     */
    public PropertyResolver(Properties props) {
        // 存入环境变量
        this.properties.putAll(System.getenv());
        // 存入Properties
        Set<String> names = props.stringPropertyNames();
        for (String name : names) {
            this.properties.put(name, props.getProperty(name));
        }
        if (logger.isDebugEnabled()) {
            List<String> keys = new ArrayList<>(this.properties.keySet());
            Collections.sort(keys);
            for (String key : keys) {
                logger.debug("PropertyResolver: {} = {}", key, this.properties.get(key));
            }
        }
        // register converters:
        converters.put(String.class, s -> s);
        converters.put(boolean.class, s -> Boolean.parseBoolean(s));
        converters.put(Boolean.class, s -> Boolean.valueOf(s));

        converters.put(byte.class, s -> Byte.parseByte(s));
        converters.put(Byte.class, s -> Byte.valueOf(s));

        converters.put(short.class, s -> Short.parseShort(s));
        converters.put(Short.class, s -> Short.valueOf(s));

        converters.put(int.class, s -> Integer.parseInt(s));
        converters.put(Integer.class, s -> Integer.valueOf(s));

        converters.put(long.class, s -> Long.parseLong(s));
        converters.put(Long.class, s -> Long.valueOf(s));

        converters.put(float.class, s -> Float.parseFloat(s));
        converters.put(Float.class, s -> Float.valueOf(s));

        converters.put(double.class, s -> Double.parseDouble(s));
        converters.put(Double.class, s -> Double.valueOf(s));

        converters.put(LocalDate.class, s -> LocalDate.parse(s));
        converters.put(LocalTime.class, s -> LocalTime.parse(s));
        converters.put(LocalDateTime.class, s -> LocalDateTime.parse(s));
        converters.put(ZonedDateTime.class, s -> ZonedDateTime.parse(s));
        converters.put(Duration.class, s -> Duration.parse(s));
        converters.put(ZoneId.class, s -> ZoneId.of(s));
    }

    /**
     * 通过key查询配置
     * `@Nullable`注解表示返回值允许为null
     *
     * @param key key
     * @return {@link String } 配置
     * @author liuzhiyong
     * @date 2023/10/18
     */
    @Nullable
    public String getProperty(String key) {
        PropertyExpr keyExpr = parsePropertyExpr(key);
        // key包含表达式
        if (keyExpr != null) {
            if (keyExpr.defaultValue() != null) {
                // 有默认值
                return getProperty(keyExpr.key(), keyExpr.defaultValue());
            } else {
                // 没有默认值
                return getRequiredProperty(keyExpr.key());
            }
        }
        // key不包含表达式
        String value = this.properties.get(key);
        if (value != null) {
            return parseValue(value);
        }
        return value;
    }

    /**
     * 通过key查询配置, 带类型转换
     *
     * @param key        key
     * @param targetType 目标类型
     * @return {@link T } 配置值
     * @author liuzhiyong
     * @date 2023/10/18
     */
    public <T> T getProperty(String key, Class<T> targetType) {
        String value = getProperty(key);
        if (value == null) {
            return null;
        }
        // 转换为指定类型
        return convert(targetType, value);
    }

    /**
     * 根据key获取配置值, 如果为null, 返回默认值
     *
     * @param key          key
     * @param defaultValue 默认值
     * @return {@link String } 配置值
     * @author liuzhiyong
     * @date 2023/10/18
     */
    public String getProperty(String key, String defaultValue) {
        String value = getProperty(key);
        return value == null ? parseValue(defaultValue) : value;
    }

    /**
     * 解析value
     *
     * @param value value ==> value有可能包含${}符号
     * @return {@link String }
     * @author liuzhiyong
     * @date 2023/10/18
     */
    String parseValue(String value) {
        PropertyExpr expr = parsePropertyExpr(value);
        if (expr == null) {
            return value;
        }
        if (expr.defaultValue() != null) {
            return getProperty(expr.key(), expr.defaultValue());
        } else {
            return getRequiredProperty(expr.key());
        }
    }


    /**
     * 解析${abc.xyz:defaultValue} 这种key
     *
     * @param key key
     * @return {@link PropertyExpr } 表达式形式的key的实体
     * @author liuzhiyong
     * @date 2023/10/18
     */
    PropertyExpr parsePropertyExpr(String key) {
        if (key.startsWith("${") && key.endsWith("}")) {
            // 是否存在defaultValue
            int n = key.indexOf(':');
            if (n == (-1)) {
                // 没有defaultValue  ==> ${key}
                String k = key.substring(2, key.length() - 1);
                return new PropertyExpr(k, null);
            } else {
                // 有defaultValue ==> ${key:default}
                String k = key.substring(2, n);
                return new PropertyExpr(k, key.substring(n + 1, key.length() - 1));
            }
        }
        return null;
    }

    /**
     * 获取必须得属性, 如果为空会抛出空指针异常
     *
     * @param key key
     * @return {@link String }
     * @author liuzhiyong
     * @date 2023/10/18
     */
    public String getRequiredProperty(String key) {
        String value = getProperty(key);
        return Objects.requireNonNull(value, "Property '" + key + "' not found.");
    }

    /**
     * 将value转为对应的类型
     *
     * @param clazz 目标类型
     * @param value      value
     * @return {@link T } 目标类型的数据
     * @author liuzhiyong
     * @date 2023/10/18
     */
    @SuppressWarnings("unchecked")
    <T> T convert(Class<T> clazz, String value) {
        Function<String, Object> fn = this.converters.get(clazz);
        if (fn == null) {
            throw new IllegalArgumentException("Unsupported value type: " + clazz.getName());
        }
        return (T) fn.apply(value);
    }

}

/**
 * 解析${abc.xyz:defaultValue} 这种key的实体
 *
 * @param key          key
 * @param defaultValue 默认值
 */
record PropertyExpr(String key, String defaultValue) {

}
